
在前面一篇原型与继承的学习笔记中,最后使用基于原型的范式实现了继承。而 ES6 引入的 class 关键字则提供了更加漂亮的语法,与 Java 等语言有点类似。还是以 Rabbit 继承 Animal 为例,使用 class 关键字的写法:
1 | class Animal { |
需要注意的几点:
class只是语法糖,在语言层面并未引入新的基本单元,从typeof Animal的结果function可以看到,本质上依然是函数。- 子类的构造器
constructor在访问this之前必须先调用父类构造器,即调用super(...)。 - 为了更多地复用代码,在子类中可以使用
super.method()调用父类的方法。 - 声明的成员方法
run,stop,hide都是定义在其原型上。以stop方法为例,等同于:Animal.prototype = function () {...}。 - 声明的静态方法则是定义在
class函数本身上,而非其原型上。 extends关键字实际上做了以下工作:- 将
Rabbit.prototype的原型设为Animal.prototype,使得Rabbit的实例可以继承Animal的实例属性 - 将
Rabbit的原型设为Animal,使得Rabbit本身可以继承Animal的静态属性
- 将
class内声明的方法之间没有逗号,。
静态属性与实例属性
在 JavaScript 中,结合 class 关键字,用类似 Java 中面向对象的思想来理解静态属性与实例属性。静态属性是挂载在 class 本身上的,而实例属性是挂载在原型 prototype 上的。我们以内置的 Object 和 Date 这两个对象为例来说明,如下图所示:

从上图可以清晰的看到:Object 和 Date 两者本身是不存在继承关系的,它们只是通过彼此的原型实现继承关系,仅此而已。
Object 的静态属性
Object.nameObject.lengthObject.prototypeObject.assignObject.getOwnPropertyDescriptorObject.getOwnPropertyDescriptorsObject.getOwnPropertyNamesObject.getOwnPropertySymbolsObject.isObject.preventExtensionsObject.sealObject.createObject.definePropertiesObject.definePropertyObject.freezeObject.getPrototypeOfObject.setPrototypeOfObject.isExtensibleObject.isFrozenObject.isSealedObject.keysObject.entriesObject.values
Object 的实例属性
constructor__defineGetter____defineSetter__hasOwnProperty__lookupGetter____lookupSetter__isPrototypeOfpropertyIsEnumerabletoStringvalueOf__proto__toLocaleString
Date 的静态属性
Date.lengthDate.nameDate.prototypeDate.nowDate.parseDate.UTC
Date 的实例属性
constructortoStringtoDateStringtoTimeStringtoISOStringtoUTCStringtoGMTStringgetDatesetDategetDaygetFullYearsetFullYeargetHourssetHoursgetMillisecondssetMillisecondsgetMinutessetMinutesgetMonthsetMonthgetSecondssetSecondsgetTimesetTimegetTimezoneOffsetgetUTCDatesetUTCDategetUTCDaygetUTCFullYearsetUTCFullYeargetUTCHourssetUTCHoursgetUTCMillisecondssetUTCMillisecondsgetUTCMinutessetUTCMinutesgetUTCMonthsetUTCMonthgetUTCSecondssetUTCSecondsvalueOfgetYearsetYeartoJSONtoLocaleStringtoLocaleDateStringtoLocaleTimeString.Symbol.toPrimitive.
instanceof 运算符
instanceof 运算符的语法如下:
1 | obj instanceof Class; |
这一运算符的本质其实是判断 Class.prototype 是否在 obj 对象的原型链上。除此之外,对于那些部署了 Symbol.hasInstance 静态方法的类,还要额外考虑。obj instanceof Class 这一算法的工作过程大致如下:
- 如果某个类部署了
Symbol.hasInstance静态方法,那么直接使用该方法。比如下面这样:
1 | // assume anything that canEat is an animal |
- 如果某个类没有部署
Symbol.hasInstance静态方法(大多数类都没有部署)。那么开始检查这个类的原型链,看Class.prototype是否等于原型链上的某个原型,即做如下比较:
1 | obj.__proto__ === Class.prototype |
还是以 Rabbit 继承 Animal 为例,来看看 instanceof 的工作过程:
1 | class Animal {} |
上面的代码中,rabbit 实例的原型链是这样的:
1 | rabbit -> Rabbit.prototpye -> Animal.prototype -> Object.prototype -> null |
rabbit instanceof Animal 执行的操作是在上面的原型链中查找,看 Animal.prototype 是否出现在原型链中,如果是则返回 true,否则返回 false。
类型检查
在 JavaScript 中,主要有 3 种方法可以用来做类型检查:
typeof主要用于检查基本数据类型,返回一个说明了所属类型的字符串。比如:
1 | alert(typeof 1); // number |
{}.toString用于检查基本数据类型,内置对象,以及部署了Symbol.toStringTag属性的对象。可以看做是增强版的typeof。示例:
1 | // 对于基本数据类型 |
instanceof用于对检查对象数据类型,尤其用于检查对象继承关系。